
package view.bean;

import java.beans.IntrospectionException;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;

import java.io.OutputStream;

import java.io.OutputStreamWriter;

import java.util.ArrayList;
import java.util.List;

import java.util.StringTokenizer;

import javax.faces.application.FacesMessage;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpSession;

import javax.xml.parsers.ParserConfigurationException;

import org.apache.myfaces.trinidad.context.RequestContext;
import org.apache.myfaces.trinidad.model.ChildPropertyTreeModel;
import org.apache.myfaces.trinidad.model.TreeModel;

import org.xml.sax.SAXException;

import view.bean.controlador.CreaModelo;
import view.bean.modelo.Atributo;
import view.bean.modelo.Clase;
import view.bean.modelo.Metodo;
import view.bean.modelo.Modelo;
import view.bean.modelo.Parametro;

public class TreeModelAdapter {
    private Object _instance = null;
    private transient TreeModel _model = null;
    
    private RequestContext afContext;
    private HttpSession sesion;
    private String webContentRoot;
    
    private String direccionJS;
    
    private Object _instanceProyectos = null;
    private transient TreeModel modelProyectos = null;

    private String listadoArchivosProy="";
    
    public TreeModelAdapter() {
        String arbolModelo = (String)RequestContext.getCurrentInstance().getPageFlowScope().get("arbolModelo");
        FacesContext fctx = FacesContext.getCurrentInstance();
        ExternalContext ectx = fctx.getExternalContext(); 
        sesion = (HttpSession) ectx.getSession(true);      
        if("SI".equals(arbolModelo))
            construirArbolModelo();
        else
            construirArbolProyectos();
    }

    public TreeModel getModel() throws IntrospectionException {
        if (_model == null) {
            _model = new ChildPropertyTreeModel(_instance, "children");       
        }
        return _model;
    }

    public void setListInstance(List instance) {
        _instance = instance;
        _model = null;
    }

    private void construirArbolModelo() {
        afContext = RequestContext.getCurrentInstance();
        ArrayList<TreeItem> rootTreeItems = new ArrayList<TreeItem>();

        String dirArchivoDC = (String)afContext.getPageFlowScope().get("dirArchivoDC");
        dirArchivoDC = dirArchivoDC.replace('/', '\\');

        try {
            //una vez copiado el archivo xmi del diagrama de clases en el servidor
            //toca leerlo y crear un modelo para mostrar en la vista siguiente como un arbol
            CreaModelo cm =
                new CreaModelo(); //lee el xml del diagrama de clases y arma el diagrama (constructor por defecto)
            Modelo modelo = modelo = cm.Hallar(dirArchivoDC); //convierte archivo xml en Modelo
           
            //nodo raiz, no tiene parent
            TreeItem treeItemModelo = new TreeItem("Modelo", null);
            rootTreeItems.add(treeItemModelo);

            ArrayList<TreeItem> treeItemModeloChildren = new ArrayList<TreeItem>();
            ArrayList<TreeItem> treeItemClaseChildren = new ArrayList<TreeItem>();

            TreeItem treeItemClase = null;
            TreeItem treeItemAtributo = null;
            TreeItem treeItemMetodo = null;
            TreeItem treeItemParametro = null;

            ArrayList<String> clases = new ArrayList<String>();
            ArrayList<String> atributosYMetodos = new ArrayList<String>();
            
            int c =0;
            for (Clase clase : modelo.getAllClases()) {
                treeItemClase = new TreeItem(clase.getNombre(), treeItemModelo);
                treeItemModeloChildren.add(treeItemClase);
            
                clases.add(clase.getNombre());
            
                ArrayList <String>atributosClass = new ArrayList<String>(); //guarda atributos de la clase 
                
            
                String met = "";//guardara la cadena de metodos para la clase actual.
                
                for (Atributo a : clase.getAtributos()) {
                    treeItemAtributo = new TreeItem(a.getTipoDato()+" "+a.getNombre(), treeItemClase);
                    treeItemClaseChildren.add(treeItemAtributo);
                    atributosClass.add(a.getNombre());
                }

                afContext.getPageFlowScope().put("atributos"+clase.getNombre(), atributosClass);
                
                for (Metodo m : clase.getMetodos()) {
                    
                    String metodo = m.getNombre() + "(";
                    
                    String signaturaMetodo = m.getNombre() + "(";
                    
                    for (Parametro p : m.getParametros()) {
                        
                        if( !p.getNombre().equalsIgnoreCase("return") ){
                            signaturaMetodo += p.getTda()+" "+p.getNombre() + ",";
                            metodo += p.getNombre() + ",";
                        }else{
                            signaturaMetodo = p.getTda() + " " + signaturaMetodo;
                        }
                        
                    } //cierre interno de parametros
                    
                    if(m.getParametros().size() > 1){
                        int ultimaComa = signaturaMetodo.length()-1;
                        signaturaMetodo = signaturaMetodo.substring(0, ultimaComa);    
                        metodo = metodo.substring(0, metodo.lastIndexOf(",") );
                    }
                    
                    signaturaMetodo += ")";
                    metodo += ")";
                    
                    
                    treeItemMetodo = new TreeItem(signaturaMetodo, treeItemClase);
                    treeItemClaseChildren.add(treeItemMetodo);
                    
                    met = met + metodo + "|"; //agrego los metodos a la clase actual

                } //cierre de metodos
                treeItemClase.setChildren(treeItemClaseChildren);
                treeItemClaseChildren = new ArrayList<TreeItem>();
                
                atributosYMetodos.add(met); //agrega atributos y metodos de la clase actual
                
                c++;
            } //cierre de clases
            treeItemModelo.setChildren(treeItemModeloChildren);

            this.setListInstance(rootTreeItems);
            
            afContext.getPageFlowScope().put("clasesPadre", clases);
            
            escribirArchivoKeyWords(dirArchivoDC, clases,atributosYMetodos);
            

        } catch (Exception e) {
            e.printStackTrace();
        }
    }


//==============================================//
    public TreeModel getModelProyectos() throws IntrospectionException {
        if (modelProyectos == null) {
            modelProyectos = new ChildPropertyTreeModel(_instanceProyectos, "children");       
        }
        return modelProyectos;
    }
    
    public void setListInstanceProyectos(List instanceProyectos) {
        _instanceProyectos = instanceProyectos;
        modelProyectos = null;
    }
    
    private void construirArbolProyectos() {
        
        String directorioActual = System.getProperty("user.dir");
        directorioActual = directorioActual.replace('\\', '/');
        directorioActual += "/Proyectos/";
        
        File dirProy = new File (directorioActual); 
        //debemos mostrar el arbol de directorios de la carpeta 'Proyectos'
        
        ArrayList<TreeItem> rootTreeItems = new ArrayList<TreeItem>();
        TreeItem treeItemPrincipal = new TreeItem("Proyectos", null, null, null);
        rootTreeItems.add(treeItemPrincipal);
        
        ArrayList<TreeItem> treeItemPrincipalChildrens = new ArrayList<TreeItem>();
        ArrayList<TreeItem> treeItemProyChildrens = new ArrayList<TreeItem>();
        ArrayList<TreeItem> treeItemSubDirChildrens = new ArrayList<TreeItem>();
        
        TreeItem treeItemProy = null;
        TreeItem treeItemSubDir = null;
        TreeItem treeItemArch = null;
        
        listarDirectorio(dirProy, ">");
        
        //System.out.println(listadoArchivosProy);
        
        StringTokenizer listTok = new StringTokenizer(listadoArchivosProy,"#");
        String tk = "";
        String nombreNodo="";
        String pathProy = "";
        String contentType = "";
        
        while(listTok.hasMoreTokens()){
            tk = listTok.nextToken();
            contentType = null;
            if(tk.startsWith(">>>")){
                //es un archivo
                nombreNodo = tk.substring(tk.indexOf(">>>")+3);
                pathProy += "/" + nombreNodo;
                  
                if(nombreNodo.contains(".java")){
                    contentType = "text/x-java-source; charset=utf-8";
                }else if(nombreNodo.contains(".xml") || nombreNodo.contains(".xmi")){
                    contentType = "text/x-xmi+xml;";
                }else{
                    contentType = null;
                }
                
                treeItemArch = new TreeItem(nombreNodo, null, directorioActual+pathProy, contentType);
                pathProy = pathProy.substring(0, pathProy.lastIndexOf("/")); //saco el nombre del archivo, por si venga otro archivo
                
                
                treeItemSubDirChildrens.add(treeItemArch);   
            }
            else if(tk.startsWith(">>")){
                //es un subDirectorio del proyecto
                //cuando llego a un directorio, puede ser porque terminaron los archivos del anterior
                //agrego el ultimo archivo antes de borrar todo
                if(treeItemSubDir != null){
                    if(treeItemSubDirChildrens != null){
                        treeItemSubDir.setChildren(treeItemSubDirChildrens);
                        //limpio la lista de hijos del directorio anterior
                        treeItemSubDirChildrens = new ArrayList<TreeItem>();    
                    }
                }
                
                nombreNodo = tk.substring(tk.indexOf(">>")+2);     
                pathProy = pathProy.substring(0, pathProy.lastIndexOf("/")+1); //saco el nombre del archivo, por si venga otro archivo
                pathProy += nombreNodo;
                
                treeItemSubDir = new TreeItem(nombreNodo, null, null,null);    
                treeItemProyChildrens.add(treeItemSubDir);
            }
            else if(tk.startsWith(">")){
                //es un proyecto
                
                //cuando llego a un proyecto, puede ser porque terminaron los archivos del directorio anterior
                //guardo el ultimo antes de borrar todo
                if(treeItemSubDir != null){
                    if(treeItemSubDirChildrens != null){
                        treeItemSubDir.setChildren(treeItemSubDirChildrens);
                    }
                }
                
                //  agrego las listas al proyecto anterior
                if(treeItemProy != null){
                    if(treeItemProyChildrens != null){
                        treeItemProy.setChildren(treeItemProyChildrens);
                        //limpio las listas, para el proyecto siguiente
                        treeItemProyChildrens = new ArrayList<TreeItem>();    
                        treeItemSubDirChildrens = new ArrayList<TreeItem>();  
                        treeItemSubDir = null;
                        treeItemArch = null;
                    }
                }
                
                //cargo el proyecto siguiente en la lista de proyectos
                nombreNodo = tk.substring(tk.indexOf(">")+1);
                pathProy = "";
                pathProy += nombreNodo + "/";
                
                treeItemProy = new TreeItem(nombreNodo, null, null, null);    
                treeItemPrincipalChildrens.add(treeItemProy);
            }
        }
        if(treeItemProyChildrens != null && treeItemProyChildrens.size() > 0){
            treeItemProy.setChildren(treeItemProyChildrens); //hijos del ultimo proyecto
        }
        treeItemPrincipal.setChildren(treeItemPrincipalChildrens); //hijos del principal
        this.setListInstanceProyectos(rootTreeItems);   
    }

    public void listarDirectorio(File f, String separador) {
        File[] ficheros = f.listFiles();
        
        for (int x = 0; x < ficheros.length; x++) {
             listadoArchivosProy += separador + ficheros[x].getName()+"#";

            if (ficheros[x].isDirectory()) {
                String nuevo_separador;
                nuevo_separador = separador + ">";
                listarDirectorio(ficheros[x], nuevo_separador);
            }
        }
    }


    public void escribirArchivoKeyWords(String dirArchivoDC, ArrayList<String> clases, ArrayList<String> metodos){
        //System.out.println("dirArchivoDC-->"+dirArchivoDC.substring(0, dirArchivoDC.lastIndexOf("\\")));
        
        FacesContext fctx = FacesContext.getCurrentInstance();
        ExternalContext ectx = fctx.getExternalContext(); 
        ServletContext servletContext = (ServletContext) ectx.getContext();
        
        //obtiene la direccion en el war de los archivos js
        String js = servletContext.getRealPath("/extend/CodeMirror-2.23/lib/util"); 
        
        String nombreProyecto = (String)afContext.getPageFlowScope().get("nombreProyecto");
        
        String directorio = js+"\\"+nombreProyecto+"\\";    //crea primero el directorio
        File dirJS = new File(directorio);
        if (!dirJS.mkdir()) {
            System.out.println("El directorio " + directorio + " no fue creado");
        }
        
        String sFichero = directorio+"javascript-hint.js"; 
        sesion.setAttribute("direccionJS", sFichero.replace("\\", "//"));
        
        this.setDireccionJS("../extend/CodeMirror-2.23/lib/util/"+nombreProyecto+"/javascript-hint.js"); //solo si es punto punto funciona 

        File fichero = new File(sFichero);  //crea el fichero

        if (fichero.exists()){
            System.out.println("El fichero " + sFichero + " ya existe");
            /*if(fichero.delete()){    //si borra el fichero retorna true
                fichero = new File(sFichero); //crea el fichero de nuevo
                System.out.println("El fichero fue borrado y creado nuevamente");
            }*/
        }else{
        
            try {
                BufferedWriter bw = new BufferedWriter(new FileWriter(sFichero));

            String antes =
"(function () {\n" + 
"  function forEach(arr, f) {\n" + 
"    for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]);\n" + 
"  }\n" + 
"  \n" + 
"  function arrayContains(arr, item) {\n" + 
"    if (!Array.prototype.indexOf) {\n" + 
"      var i = arr.length;\n" + 
"      while (i--) {\n" + 
"        if (arr[i] === item) {\n" + 
"          return true;\n" + 
"        }\n" + 
"      }\n" + 
"      return false;\n" + 
"    }\n" + 
"    return arr.indexOf(item) != -1;\n" + 
"  }\n" + 
"\n" + 
"  function scriptHint(editor, keywords, getToken) {\n" + 
"    // Find the token at the cursor\n" + 
"    var cur = editor.getCursor(), token = getToken(editor, cur), tprop = token;\n" + 
"    // If it's not a 'word-style' token, ignore the token.\n" + 
"		if (!/^[\\w$_]*$/.test(token.string)) {\n" + 
"      token = tprop = {start: cur.ch, end: cur.ch, string: \"\", state: token.state,\n" + 
"                       className: token.string == \".\" ? \"property\" : null};\n" + 
"    }\n" + 
"    // If it is a property, find out what it is a property of.\n" + 
"    while (tprop.className == \"property\") {\n" + 
"      tprop = getToken(editor, {line: cur.line, ch: tprop.start});\n" + 
"      if (tprop.string != \".\") return;\n" + 
"      tprop = getToken(editor, {line: cur.line, ch: tprop.start});\n" + 
"      if (tprop.string == ')') {\n" + 
"        var level = 1;\n" + 
"        do {\n" + 
"          tprop = getToken(editor, {line: cur.line, ch: tprop.start});\n" + 
"          switch (tprop.string) {\n" + 
"          case ')': level++; break;\n" + 
"          case '(': level--; break;\n" + 
"          default: break;\n" + 
"          }\n" + 
"        } while (level > 0)\n" + 
"        tprop = getToken(editor, {line: cur.line, ch: tprop.start});\n" + 
"				if (tprop.className == 'variable')\n" + 
"					tprop.className = 'function';\n" + 
"				else return; // no clue\n" + 
"      }\n" + 
"      if (!context) var context = [];\n" + 
"      context.push(tprop);\n" + 
"    }\n" + 
"    return {list: getCompletions(token, context, keywords),\n" + 
"            from: {line: cur.line, ch: token.start},\n" + 
"            to: {line: cur.line, ch: token.end}};\n" + 
"  }\n" + 
"\n" + 
"  CodeMirror.javascriptHint = function(editor) {\n" + 
"    return scriptHint(editor, javaKeywords,\n" + 
"                      function (e, cur) {return e.getTokenAt(cur);});\n" + 
"  }\n" + 
"\n" + 
"  function getCoffeeScriptToken(editor, cur) {\n" + 
"  // This getToken, it is for coffeescript, imitates the behavior of\n" + 
"  // getTokenAt method in javascript.js, that is, returning \"property\"\n" + 
"  // type and treat \".\" as indepenent token.\n" + 
"    var token = editor.getTokenAt(cur);\n" + 
"    if (cur.ch == token.start + 1 && token.string.charAt(0) == '.') {\n" + 
"      token.end = token.start;\n" + 
"      token.string = '.';\n" + 
"      token.className = \"property\";\n" + 
"    }\n" + 
"    else if (/^\\.[\\w$_]*$/.test(token.string)) {\n" + 
"      token.className = \"property\";\n" + 
"      token.start++;\n" + 
"      token.string = token.string.replace(/\\./, '');\n" + 
"    }\n" + 
"    return token;\n" + 
"  }\n" + 
"\n" + 
"  CodeMirror.coffeescriptHint = function(editor) {\n" + 
"    return scriptHint(editor, coffeescriptKeywords, getCoffeeScriptToken);\n" + 
"  }\n" + 
"\n" + 
"  var stringProps = (\"charAt charCodeAt indexOf lastIndexOf substring substr slice trim trimLeft trimRight \" +\n" + 
"                     \"toUpperCase toLowerCase split concat match replace search\").split(\" \");\n" + 
"  var arrayProps = (\"length concat join splice push pop shift unshift slice reverse sort indexOf \" +\n" + 
"                    \"lastIndexOf every some filter forEach map reduce reduceRight \").split(\" \");\n" + 
"  var funcProps = \"prototype apply call bind\".split(\" \");\n" + 
"  var javascriptKeywords = (\"break case catch continue debugger default delete do else false finally for function \" +\n" + 
"                  \"if in instanceof new null return switch throw true try typeof var void while with\").split(\" \");\n" + 
"  var coffeescriptKeywords = (\"AND break catch class continue delete do else extends false finally for \" +\n" + 
"                  \"if in instanceof isnt new no not null of off on OR return switch then throw true try typeof until void while with yes\").split(\" \");\n" +
                "//para las palabras reservadas de java\n" + 
                "  var javaKeywords = (\"abstract|continue|for|new|switch|assert|default|goto|package|synchronized|boolean|do|if|private|\"+\n" + 
                "					\"this|break|double|implements|protected|throw|byte|else|import|public|throws|case|enum|instanceof|\"+\n" + 
                "					\"return|transient|catch|extends|int|short|try|char|final|interface|static|void|class|finally|long|\"+\n" + 
                "					\"strictfp|volatile|const|float|native|super|while\").split(\"|\");\n";

                bw.write(antes);

                String funcionGatherCompletion =
                "    function gatherCompletions(obj) {\n" + 
                "      if (obj == \"projectKeyWords\") forEach(projectKeyWords,maybeAdd);\n";

                String ifContext = "";

                String projkw = "  var projectKeyWords = (\"PRE|POST|OR|AND|NOT|";
                for(int i=0; i < clases.size(); i++){
                    projkw = projkw + clases.get(i)+ "|";
                    //si la clase no tiene metodos, el string "metodos" vendra vacio
                    if (!"".equalsIgnoreCase(metodos.get(i))) {

                        String clasekw = "  var " + clases.get(i) + "KeyWords = (\"";


                        clasekw += metodos.get(i);
                        clasekw = clasekw.substring(0, clasekw.lastIndexOf("|")) + "\").split(\"|\");";


                        //System.out.println(clasekw);

                        funcionGatherCompletion +=
                                "      else if (obj == \"" + clases.get(i) + "\") forEach(" + clases.get(i) +
                                "KeyWords" + ",maybeAdd);\n";

                        if (i == 0)
                            ifContext += "      if ";
                        else
                            ifContext += "      else if ";

                        ifContext += "(obj.string == \"" + clases.get(i) + "\")\n" +
                                "        base = \"" + clases.get(i) + "\";\n";

                        bw.write(clasekw + "\n");
                    }
                }
                
                projkw = projkw.substring(0, projkw.lastIndexOf("|")) + "\").split(\"|\");"; 
                //System.out.println(projkw);
                bw.write(projkw + "\n");

                funcionGatherCompletion +=
                                "      else if (typeof obj == \"string\") forEach(stringProps, maybeAdd);\n" + 
                                "      else if (obj instanceof Array) forEach(arrayProps, maybeAdd);\n" + 
                                "      else if (obj instanceof Function) forEach(funcProps, maybeAdd);\n" + 
                                "         //for (var name in obj) maybeAdd(name);\n" + 
                                "    }\n";

                String despues =
"  function getCompletions(token, context, keywords) {\n" +     
"    var found = [], start = token.string;\n" + 
"    function maybeAdd(str) {\n" + 
"      if (str.indexOf(start) == 0 && !arrayContains(found, str)) found.push(str);\n" + 
"    }\n" + 

    funcionGatherCompletion +

"\n" + 
"    if (context) {\n" + 
"      // If this is a property, see if it belongs to some object we can\n" + 
"      // find in the current environment.\n" + 
"      var obj = context.pop(), base;\n" + 

    ifContext +

"      else if (obj.className == \"variable\")\n" + 
"        base = window[obj.string];\n" + 
"      else if (obj.className == \"string\")\n" + 
"        base = \"\";\n" + 
"      else if (obj.className == \"atom\")\n" + 
"        base = 1;\n" + 
"      else if (obj.className == \"function\") {\n" + 
"        if (window.jQuery != null && (obj.string == '$' || obj.string == 'jQuery') &&\n" + 
"            (typeof window.jQuery == 'function'))\n" + 
"          base = window.jQuery();\n" + 
"        else if (window._ != null && (obj.string == '_') && (typeof window._ == 'function'))\n" + 
"          base = window._();\n" + 
"      }\n" + 
"      while (base != null && context.length)\n" + 
"        base = base[context.pop().string];\n" + 
"      if (base != null) gatherCompletions(base);\n" + 
"    }\n" + 
"    else {\n" + 
"      // If not, just look in the window object and any local scope\n" + 
"      // (reading into JS mode internals to get at the local variables)\n" + 
"      for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name);\n" + 
"      gatherCompletions(\"projectKeyWords\"); //le pedimos que traiga las palabras reservadas de projectKeyWords cuando no escribimos nada\n" + 
"      forEach(keywords, maybeAdd);\n" + 
"    }\n" + 
"    return found;\n" + 
"  }\n" + 
"})();\n";


                bw.write(despues);

                // Hay que cerrar el fichero
                bw.close();
                
                //cargamos en el sessionScope el archivo entero
                sesion.setAttribute("javasScriptHints", despues);
                
            } catch (IOException ioe) {
                ioe.printStackTrace();
            }
        
        
        }
    }

    public void setDireccionJS(String direccionJS) {
        this.direccionJS = direccionJS;
    }

    public String getDireccionJS() {
        return direccionJS;
    }

    public void sayHello(FacesContext context, OutputStream out) {
        /*         OutputStreamWriter w = new OutputStreamWriter(out, "UTF-8");

        w.write("hello");
        // The stream is automatically closed, but since we wrapped it,
        // we'd better flush our writer
        w.flush(); */

        String path = context.getExternalContext().getRequestParameterMap().get("path");

        try {
            File file = new File(path);

            FileInputStream fdwd;

            byte[] bt;

            try {

                System.out.println(file.getCanonicalPath());

                System.out.println(file.getAbsolutePath());

                fdwd = new FileInputStream(file);

                int checkline;

                while ((checkline = fdwd.available()) > 0) {

                    bt = new byte[checkline];

                    int rst = fdwd.read(bt);

                    out.write(bt, 0, bt.length);

                    if (rst == -1)

                        break;

                }

                out.flush();

            } catch (IOException e) {

                System.out.println(FacesMessage.SEVERITY_ERROR +
                                   "File cannot be downloaded , Please contact administrator.");

            }

        } catch(Exception e) {
            e.printStackTrace();
        }
    }
    
    
    public void sayHello2(FacesContext context, OutputStream out){
        
    }
}
